
#ifndef __M_MATCHER_H__
#define __M_MATCHER_H__

#include"util.hpp"
#include "online.hpp"
#include "db.hpp"
#include "room.hpp"
#include<mutex>
#include<condition_variable>
#include<list>

template<class T>
class match_queue{
    private:
    std::list<T> _list;//匹配数组
    std::mutex _mutex;//互斥锁
    std::condition_variable _cond;//环境变量!!!!!!!!!!

    public :
    int size()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.size();
    }//获取元素数量

    bool empty()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        return _list.empty();
    }//判断是否是空

    void wait()
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _cond.wait(lock);//阻塞！！！！！！！！！！！！！！！！！！！！！！ _cond.wait(lock);
    }
    //等待--阻塞线程

    void push(const T&data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        _list.push_back(data);
        _cond.notify_all();//唤醒！！！！！！！！！！！！！！！！！！！！！_cond.notify_all();
    }
    //插入数据并唤醒线程


    bool pop(T& data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
        if(_list.empty())
        return false;
        data = _list.front();
        _list.pop_front();
        return true;
    }
    //获取队列的头

    
    void remove (T& data)
    {
        std::unique_lock<std::mutex> lock(_mutex);
       _list.remove(data);
    }
    //删除指定数据

};


class matcher
{
    private:
    match_queue<uint64_t> _q_normal;//普通等级
    match_queue<uint64_t> _q_higher;//高手等级
    match_queue<uint64_t> _q_super;//超级等级

    std::thread th_normal;//设置的关于普通等级的线程
    std::thread th_higher;//设置的关于高手等级的线程
    std::thread th_super;//设置的关于超级等级的线程

    room_manager *_rm;//房间管理类
    user_table  *_ut;//数据库操作类
    online_manager *_om;//在线管理类


    //线程的入口函数
    void handle_match(match_queue<uint64_t> &mq)//记得要使用引用&-------------------------!!!!!!!!!!!!!!!!
    {
        //死循环！！！！！！！！
       while(1) {
                //1. 判断队列人数是否大于2，<2则阻塞等待
                while (mq.size() < 2) {
                    mq.wait();//使用match_queue类封装的wait函数让线程进行等待
                }    
                //2. 走下来代表人数够了，出队两个玩家
                uint64_t uid1, uid2;
                bool ret = mq.pop(uid1);
                if (ret == false) { 
                    continue; 
                }

                ret = mq.pop(uid2);
                if (ret == false) { 
                    this->add(uid1); //如果第二个玩家没成功出队，那么一定要把第一个玩家重新返回队列中！！！！！！！！！
                    continue; 
                }

                //3. 校验两个玩家是否在线，如果有人掉线，则要吧另一个人重新添加入队列
                wsserver_t::connection_ptr conn1 = _om->get_conn_from_hall(uid1);//直接获取用户对应的通信连接
                if (conn1.get() == nullptr) {
                    this->add(uid2); 
                    continue;
                }
                wsserver_t::connection_ptr conn2 = _om->get_conn_from_hall(uid2);
                if (conn2.get() == nullptr) {
                    this->add(uid1); 
                    continue;
                }
                //4. 为两个玩家创建房间，并将玩家加入房间中
                room_ptr rp = _rm->create_room(uid1, uid2);
                if (rp.get() == nullptr) {
                    this->add(uid1);
                    this->add(uid2);
                    continue;
                }

                //5. 对两个玩家进行响应
                Json::Value resp;
                resp["optype"] = "match_success";
                resp["result"] = true;
                std::string body;
                json_util::serialize(resp, body);//记得要使用util：：的serialize
                conn1->send(body);//通过conn通信连接来send信息到两个用户！！！
                conn2->send(body);
            }
    }
    void th_normal_entry() { return handle_match(_q_normal); }
    void th_higher_entry() { return handle_match(_q_higher); }
    void th_super_entry() { return handle_match(_q_super);}

    public: 
    matcher(room_manager *rm,user_table *ut,online_manager *om)
    :_rm(rm)
    ,_ut(ut)
    ,_om(om)
    ,th_normal(std::thread(&matcher::th_normal_entry,this))//////////////////////////初始化线程，要带有线程启动函数，还有this指针
    ,th_higher(std::thread(&matcher::th_higher_entry,this)) 
    ,th_super(std::thread(&matcher::th_super_entry,this)) 
    { DBG_LOG("游戏匹配模块初始化完成！！\n");}
    bool add(uint64_t uid)
    { 
         Json::Value user; 
        //1.要先获取该用户的分数----从数据库中获取！！！
        bool ret = _ut->select_id(user,uid);
        if(ret == false)
        {
            DBG_LOG("获取用户信息失败\n");
            return false;
        }
        //2.根据分数划分score
        uint64_t score = user["score"].asInt64();
        if(score < 2000)
        {
           _q_normal.push(uid);
        }
        else if(score>= 2000 && score < 3000)
        {
           _q_higher.push(uid);
        }
        else
        {
           _q_super.push(uid);
        }

        return true;
    }

    bool del(uint64_t uid)
    {
        Json::Value user;
        //1.要先获取该用户的分数----从数据库中获取！！！
        bool ret = _ut->select_id(user,uid);
        if(ret == false)
        {
            DBG_LOG("获取用户信息失败\n");
            return false;
        }

        //2.根据分数划分score
        uint64_t score = user["score"].asInt64();
        if(score < 2000)
        {
           _q_normal.remove(uid);
        }
        else if(score>= 2000 && score < 3000)
        {
           _q_higher.remove(uid);
        }
        else
        {
           _q_super.remove(uid);
        }
        return true;
    }
    
};

#endif